package com.my.study.config;

import com.my.study.aop.LogAspects;
import com.my.study.aop.MathCalculator;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

/**
 * AOP【动态代理】
 *     指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式。
 * 通知方法：
 *      前置通知：目标方法运行之前运行
 *      后置通知：目标方法运行结束之后运行（无论目标方法正常结束还是异常结束都会运行）
 *      返回通知：目标方法正常返回之后运行
 *      异常通知：目标方法运行出现异常后运行
 *      环绕通知：动态代理，手动推进目标方法运行
 * 
 * 
 * <p>AOP原理
 * AOP生效的秘密在于注解{@link EnableAspectJAutoProxy},下面就从它开始分析：
 * 1、{@link EnableAspectJAutoProxy}是什么？
 *    它里面包含注解@Import(AspectJAutoProxyRegistrar.class)：给容器中导入 AspectJAutoProxyRegistrar
 *    利用{@code AspectJAutoProxyRegistrar}自定义给容器中注册bean -> {@link AnnotationAwareAspectJAutoProxyCreator}
 *
 * 2、{@link AnnotationAwareAspectJAutoProxyCreator}继承关系
 *      -> AspectJAwareAdvisorAutoProxyCreator
 *          -> AbstractAdvisorAutoProxyCreator
 *              -> AbstractAutoProxyCreator
 *                  -> ProxyProcessorSupport
 *                  -> SmartInstantiationAwareBeanPostProcessor （在bean初始化完成前后做事情 ）
 *                  -> BeanFactoryAware
 *
 *  流程：
 *  1、传入配置类，创建IOC容器
 *  2、注册配置类，调用AnnotationConfigApplicationContext.refresh()方法，刷新容器;
 *  3、注册bean后置处理器，调用AbstractApplicationContext.registerBeanPostProcessors()方法，方便拦截bean的创建
 *      1）先获取ioc容器中已经定义了的需要创建对象的所有BeanPostProcessor
 *      2）给容器中加入其它BeanPostProcessor
 *      3）优先注册实现了PriorityOrdered接口的BeanPostProcessor
 *      4）再注册实现了Ordered接口的BeanPostProcessor
 *      5）最后，注册普通的BeanPostProcessor
 *      6）注册BeanPostProcessor，实际上就是创建BeanPostProcessor对象并保存在容器中
 *          创建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
 *          1）创建bean实例
 *          2）populateBean，给对象属性赋值
 *          3）initializeBean(),初始化bean
 *              1）invokeAwareMethods()：处理Aware接口的方法回调
 *              2）applyBeanPostProcessorsBeforeInitialization()：调用后置处理器BeanPostProcessor的before
 *              3）invokeInitMethods()：执行自定义的初始化
 *              4）applyBeanPostProcessorsAfterInitialization()：调用后置处理器BeanPostProcessor的after
 *          4）BeanPostProcessor成功【AnnotationAwareAspectJAutoProxyCreator】
 *      7）把BeanPostProcessor注册到BeanFactory中
 *  4、调用AbstractApplicationContext.finishBeanFactoryInitialization(beanFactory)方法，完成BeanFactory初始化工作，创建剩余单实例
 *      1）遍历获取容器中所有的bean，依次创建对象 DefaultListableBeanFactory.getBean(beanName)
 *          getBean()->doGetBean()->getSingleton()->
 *      2）再创建bean
 *          1）先从缓存中获取到当前bean，如果有则说明之前被创建了，直接使用，否则再创建；
 *              只要创建的bean都会被缓存起来
 *          2）createBean()创建bean
 *              1）resolveBeforeInstantiation(beanName, mbdToUse)实例化之前解析
 *                希望后置处理器BeanPostProcessors在此能返回一个代理对象：如果能则直接返回，如果不能就继续
 *                  1）尝试返回对象
 *                      bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
 *                      if (bean != null) {
 *                          bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
 *                      }
 *
 *
 *
 *              2）调用doCreateBean(beanName, mbdToUse, args)真正去创建一个bean实例
 *
 *      3）
 *  AnnotationAwareAspectJAutoProxyCreator【SmartInstantiationAwareBeanPostProcessor】的作用：
 *  1、每个bean创建之前，都会调用postProcessBeforeInstantiation()方法，判断是否要增强
 *      1）判断当前bean是否在Map集合advisedBeans中（保存需要增强的bean）
 *      2）判断当前bean是否基础设施类（Advice、Pointcut、Advisor、AopInfrastructureBean）或是是否是切面(@Aspects)
 *      3）是否跳过
 *          1）获取候选的增强器（切面里面的通知方法） 【List<Advisor> candidateAdvisors】
 *          每个封装的通知方法增强器是【InstantiationModelAwarePointcutAdvisorImpl】
 *          2）返回false
 *
 *  2、创建bean之后，都会调用postProcessAfterInitialization()方法
 *     postProcessAfterInitialization()->wrapIfNecessary() ->createProxy()
 *     1）获取当前bean的所有增强器（通知方法） 封装为【Object[] specificInterceptors】
 *          1）找到所有的候选增强器 （也就是：找到哪些通知方法是需要切入当前bean方法的）
 *          2）获取到能在当前bean使用的增强器并排序
 *      2）保存当前bean到 advisedBeans中，v->true
 *      3）为其创建代理对象 createProxy()
 *          1）获取所以可以切入当前bean的增强器（通知方法）
 *          2）获取到的增强器保存到ProxyFactory中
 *          3）使用ProxyFactory创建代理对象
 *              JdkDynamicAopProxy(config)；
 *              ObjenesisCglibAopProxy(config)
 *       4）给容器中返回当前组件使用的cglib增强了的对象
 *       5）以后容器获取的就是这个bean的代理对象，执行目标方法的时候，代理对象就是通知方法
 *   3、目标方法执行
 *      容器中保存了组件的代理对象（cglib增强后的对象），这个对象里面保存了详细信息（增强器、目标对象...）
 *      1）首先进入了CglibAopProxy.DynamicAdvisedInterceptor#intercept();拦截目标方法的执行
 *      2）根据ProxyFactory获取将要执行的目标方法 拦截器链
 *      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
 *      
 *      
 *      
 * 
 * @author Carlos
 * @version 1.0.0
 * @date 2022/10/18 21:45
 */
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAop {

    @Bean
    public MathCalculator mathCalculator() {
        return new MathCalculator();
    }

    @Bean
    public LogAspects logAspects() {
        return new LogAspects();
    }
}
